﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace MadMediaManager.WPFSupport.Services.Helpers
{
    /// <summary>
    /// This class is an implementation detail of the WeakEventPublisher class.
    /// </summary>
    internal class MessageToActionsMap
    {
        #region AddAction

        /// <summary>
        /// Adds an action to the list.
        /// </summary>
        /// <param name="target">The target object to invoke, or null.</param>
        /// <param name="method">The method to invoke.</param>
        /// <param name="actionType">The type of the Action delegate.</param>
        internal void AddAction(object target, MethodInfo method, Type actionType)
        {
            if (method == null)
                throw new ArgumentNullException("method");

            lock (_map)
            {
                if (!_map.ContainsKey(actionType))
                    _map[actionType] = new List<WeakAction>();

                _map[actionType].Add(new WeakAction(target, method, actionType));
            }
        }

        /// <summary>
        /// Adds an action to the list.
        /// </summary>
        /// <param name="target">The target object to invoke, or null.</param>
        /// <param name="method">The method to invoke.</param>
        /// <param name="actionType">The type of the Action delegate.</param>
        internal void RemoveAction(object target, MethodInfo method, Type actionType)
        {
            if (method == null)
                throw new ArgumentNullException("method");

            lock (_map)
            {
                if (!_map.ContainsKey(actionType))
                    return;

                _map[actionType].Remove(new WeakAction(target, method, actionType));
            }
        }

        #endregion // AddAction

        /// <summary>
        /// Gets the list of actions to be invoked for the specified message
        /// </summary>
        /// <returns>Returns a list of actions that are registered to the specified message</returns>
        internal List<Delegate> GetActions<T>()
        {
            List<Delegate> actions;
            var actionType = typeof(T);
            lock (_map)
            {
                if (!_map.ContainsKey(actionType))
                    return null;

                List<WeakAction> weakActions = _map[actionType];
                actions = new List<Delegate>(weakActions.Count);
                for (int i = weakActions.Count - 1; i > -1; --i)
                {
                    WeakAction weakAction = weakActions[i];
                    if (weakAction == null)
                        continue;

                    Delegate action = weakAction.CreateAction();
                    if (action != null)
                    {
                        actions.Add(action);
                    }
                    else
                    {
                        // The target object is dead, so get rid of the weak action.
                        weakActions.Remove(weakAction);
                    }
                }

                // Delete the list from the map if it is now empty.
                if (weakActions.Count == 0)
                    _map.Remove(actionType);
            }

            // Reverse the list to ensure the callbacks are invoked in the order they were registered.
            actions.Reverse();

            return actions;
        }
        
        #region Fields

        private readonly Dictionary<Type, List<WeakAction>> _map = new Dictionary<Type, List<WeakAction>>();

        #endregion // Fields
    }
}
